commonlibsse_ng\re\b/
BSFixedString.rs1use crate::re::BSStringPool::{self, StringFormat, U16};
2use core::{marker::PhantomData, mem, ptr};
3
4#[repr(transparent)]
17pub struct BSFixedStringInternal<T>
18where
19 T: StringFormat,
20{
21 data: *const T::Unit,
23 marker: PhantomData<Box<T::Unit>>,
24}
25
26impl<T> BSFixedStringInternal<T>
27where
28 T: StringFormat,
29{
30 pub fn try_acquire(&self) {
34 if let Some(proxy) = unsafe { self.get_proxy() } {
35 proxy.acquire();
36 }
37 }
38
39 pub fn try_release(&mut self) {
44 if !self.data.is_null() {
45 unsafe { BSStringPool::Entry::<T>::release(&self.data) };
46 self.data = ptr::null();
47 }
48 }
49
50 pub const unsafe fn get_proxy(&self) -> Option<&mut BSStringPool::Entry<T>> {
57 if self.data.is_null() {
58 return None;
59 }
60
61 let proxy_ptr = unsafe { self.data.sub(mem::size_of::<BSStringPool::Entry<T>>()) }
62 as *mut BSStringPool::Entry<T>;
63 unsafe { proxy_ptr.as_mut() }
64 }
65
66 #[inline]
70 pub fn count_bytes_with_null(&self) -> u32 {
71 unsafe { self.get_proxy().map_or(0, |proxy| proxy.len()) }
72 }
73
74 #[inline]
78 pub fn is_empty(&self) -> bool {
79 self.count_bytes_with_null() == 0
80 }
81
82 #[inline]
84 pub fn as_bytes_with_null(&self) -> &[u8] {
85 unsafe {
86 core::slice::from_raw_parts(
87 self.data.cast::<u8>(),
88 self.count_bytes_with_null() as usize,
89 )
90 }
91 }
92}
93
94impl<T: StringFormat> Clone for BSFixedStringInternal<T> {
95 #[inline]
96 fn clone(&self) -> Self {
97 let cloned = Self { data: self.data, marker: PhantomData };
98 cloned.try_acquire();
99 cloned
100 }
101}
102
103impl PartialOrd for BSFixedString {
104 #[inline]
105 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
106 Some(self.cmp(other))
107 }
108}
109
110impl Ord for BSFixedString {
111 #[inline]
112 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
113 self.as_bytes_with_null().cmp(other.as_bytes_with_null())
114 }
115}
116
117impl core::hash::Hash for BSFixedString {
118 #[inline]
119 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
120 self.as_bytes_with_null().hash(state);
121 }
122}
123
124impl<T: StringFormat> Drop for BSFixedStringInternal<T> {
125 #[inline]
126 fn drop(&mut self) {
127 self.try_release();
128 }
129}
130
131pub use u8_bytes::{BSFixedString, ctor8};
132pub use u16_wide::{BSFixedStringW, ctor16};
133
134mod u8_bytes {
135 use super::*;
136 use crate::re::BSStringPool::U8;
137 use core::ffi::{CStr, c_char};
138 use core::ops::Deref;
139 use core::{fmt, str};
140
141 pub type BSFixedString = BSFixedStringInternal<U8>;
154
155 #[commonlibsse_ng_derive_internal::relocate_fn(se_id = 67819, ae_id = 69161)]
160 #[allow(clippy::use_self)]
161 pub unsafe fn ctor8(data: *const c_char) -> BSFixedString {}
162
163 pub const EMPTY_C_CHAR: *const c_char = c"".as_ptr();
165
166 impl BSFixedString {
167 pub const DEFAULT: Self = Self { data: EMPTY_C_CHAR, marker: PhantomData };
169
170 #[inline]
175 pub fn new(data: &CStr) -> Self {
176 unsafe { Self::new_unchecked(data.as_ptr()) }
177 }
178
179 #[inline]
187 pub unsafe fn new_unchecked(data: *const c_char) -> Self {
188 unsafe { ctor8(data) }
189 }
190
191 pub fn as_c_str(&self) -> &CStr {
197 if let Some(proxy) = unsafe { self.get_proxy() } {
198 unsafe {
199 return CStr::from_ptr(proxy.as_raw());
200 }
201 }
202 unsafe { CStr::from_ptr(EMPTY_C_CHAR) }
203 }
204
205 #[inline]
207 pub fn to_str(&self) -> Option<&str> {
208 core::str::from_utf8(self.as_bytes_with_null()).ok()
209 }
210
211 #[inline]
213 pub fn contains(&self, rhs: &CStr) -> bool {
214 let self_bytes = self.as_bytes_with_null();
215 let rhs_bytes = rhs.to_bytes();
216 let rhs_len = rhs_bytes.len();
217
218 if rhs_len > self_bytes.len() {
219 return false;
220 }
221
222 self_bytes.windows(rhs_len).any(|window| window == rhs_bytes)
223 }
224 }
225
226 impl PartialEq for BSFixedString {
227 #[inline]
228 fn eq(&self, other: &Self) -> bool {
229 if self.is_empty() && other.is_empty() {
230 true
231 } else {
232 self.as_c_str() == other.as_c_str()
233 }
234 }
235 }
236
237 impl Eq for BSFixedString {}
238
239 impl Default for BSFixedString {
240 #[inline]
241 fn default() -> Self {
242 Self { data: EMPTY_C_CHAR, marker: PhantomData }
243 }
244 }
245
246 impl Deref for BSFixedString {
247 type Target = CStr;
248
249 #[inline]
250 fn deref(&self) -> &Self::Target {
251 self.as_c_str()
252 }
253 }
254
255 impl fmt::Display for BSFixedString {
256 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257 write!(f, "{}", self.as_c_str().to_string_lossy())
258 }
259 }
260
261 impl fmt::Debug for BSFixedString {
262 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
263 write!(
264 f,
265 "{:?}",
266 str::from_utf8(self.as_bytes_with_null()).unwrap_or("<Invalid UTF-8>")
267 )
268 }
269 }
270
271 impl From<&CStr> for BSFixedString {
272 fn from(value: &CStr) -> Self {
273 Self::new(value)
274 }
275 }
276}
277
278mod u16_wide {
279 use super::*;
280 use core::fmt;
281 use std::string::FromUtf16Error;
282
283 pub type BSFixedStringW = BSFixedStringInternal<U16>;
292
293 #[commonlibsse_ng_derive_internal::relocate_fn(se_id = 67834, ae_id = 69176)]
295 #[allow(clippy::use_self)]
296 pub unsafe fn ctor16(data: *const u16) -> BSFixedStringW {}
297
298 impl BSFixedStringW {
299 pub const EMPTY: &'static [u16] = &[];
301
302 #[inline]
305 pub const unsafe fn new_unchecked(data: *const u16) -> Self {
306 if !data.is_null() {
307 Self { data, marker: PhantomData }
308 } else {
309 Self { data: Self::EMPTY.as_ptr(), marker: PhantomData }
310 }
311 }
312
313 #[inline]
315 pub fn as_wide(&self) -> &[u16] {
316 unsafe { self.get_proxy() }.map_or(&[], |proxy| unsafe {
317 core::slice::from_raw_parts(proxy.as_raw(), proxy.len() as usize)
318 })
319 }
320
321 #[inline]
323 pub fn to_string_lossy(&self) -> String {
324 String::from_utf16_lossy(self.as_wide())
325 }
326
327 #[inline]
331 pub fn to_string(&self) -> Result<String, FromUtf16Error> {
332 String::from_utf16(self.as_wide())
333 }
334 }
335
336 impl PartialEq for BSFixedStringW {
337 #[inline]
338 fn eq(&self, other: &Self) -> bool {
339 self.as_wide() == other.as_wide()
340 }
341 }
342
343 impl Default for BSFixedStringW {
344 #[inline]
345 fn default() -> Self {
346 Self { data: Self::EMPTY.as_ptr(), marker: PhantomData }
347 }
348 }
349
350 impl fmt::Display for BSFixedStringW {
351 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352 write!(f, "{}", self.to_string_lossy())
353 }
354 }
355
356 impl fmt::Debug for BSFixedStringW {
357 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
358 write!(f, "{:?}", self.to_string_lossy())
359 }
360 }
361}
362
363